/**
 * @file telemetry_data.h
 * @brief 遥信数据模块 - 处理遥信相关的所有数据类型
 * @details 本头文件定义了104协议中遥信数据类型的结构体和操作函数，
 *          包括单点信息、双点信息、带时标的遥信信息、故障事件信息等。
 *          支持完整的数据创建、转换和验证功能。
 * 
 * @author zyb
 * @date 2025-10-09
 * @version 1.0
 * 
 * @note 所有数据类型都遵循IEC 60870-5-104协议规范
 * @warning 遥信数据涉及设备状态，需要严格验证
 * 
 * @see protocol_types.h
 * @see error_codes.h
 * @see validation_utils.h
 */

#ifndef TELEMETRY_DATA_H
#define TELEMETRY_DATA_H

#include "../../common/types/include/protocol_types.h"

#ifdef __cplusplus
extern "C" {
#endif

/* ==================== 遥信数据类型定义 ==================== */

/* 单点信息结构和双点信息结构已在protocol_types.h中定义 */

/**
 * @brief 带CP56Time2a时标的单点信息结构 (M_SP_TB_1)
 */
typedef struct {
    info_addr_t info_addr;  /* 信息对象地址 */
    siq_t siq;              /* 单点信息品质描述词 */
    cp56time2a_t time;      /* 时间戳 */
} single_point_info_with_time_t;

/**
 * @brief 带CP56Time2a时标的双点信息结构 (M_DP_TB_1)
 */
typedef struct {
    info_addr_t info_addr;  /* 信息对象地址 */
    diq_t diq;              /* 双点信息品质描述词 */
    cp56time2a_t time;      /* 时间戳 */
} double_point_info_with_time_t;

/**
 * @brief 故障事件信息结构 (M_FT_NA_1)
 */
typedef struct {
    u8 di_count;            /* 遥信个数 */
    u8 di_type;             /* 遥信类型 */
    u16 di_addr;            /* 故障遥信点号 */
    u8 di_value;            /* 遥信值 */
    cp56time2a_t fault_time; /* 故障时刻时标 */
    
    u8 ai_count;            /* 遥测个数 */
    u8 ai_type;             /* 遥测类型 */
    struct {
        info_addr_t addr;   /* 遥测信息体地址 */
        float value;        /* 故障时刻数据 */
    } ai_data[32];          /* 遥测数据数组，最多32个 */
} fault_event_info_t;

/* ==================== 遥信数据转换函数 ==================== */

/**
 * @brief 单点信息转换为字节数组
 * @details 将单点信息结构转换为104协议规定的字节数组格式。
 *          按照协议规范进行数据序列化。
 * 
 * @param info 单点信息结构，不能为NULL
 * @param buffer 输出缓冲区，不能为NULL
 * @param buffer_size 缓冲区大小
 * @param actual_length 实际写入长度输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 缓冲区太小
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 * 
 * @note 按照104协议规范进行数据序列化
 * @warning 缓冲区大小必须足够容纳转换后的数据
 * 
 * @see bytes_to_single_point_info
 * @see validate_single_point_info
 * @see buffer_write_u32
 * @see buffer_write_u8
 */
protocol_error_t single_point_info_to_bytes(const single_point_info_t* info,
                                          u8* buffer, u32 buffer_size, u32* actual_length);

/**
 * @brief 字节数组转换为单点信息
 * @details 将104协议规定的字节数组格式转换为单点信息结构。
 *          按照协议规范进行数据反序列化。
 * 
 * @param data 字节数组，不能为NULL
 * @param length 数据长度
 * @param info 单点信息结构输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 数据长度不足
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 * 
 * @note 按照104协议规范进行数据反序列化
 * @warning 数据长度必须足够容纳单点信息
 * 
 * @see single_point_info_to_bytes
 * @see validate_single_point_info
 * @see buffer_read_u32
 * @see buffer_read_u8
 */
protocol_error_t bytes_to_single_point_info(const u8* data, u32 length,
                                           single_point_info_t* info);

/**
 * @brief 验证单点信息
 * @details 验证单点信息结构的完整性和有效性。
 *          检查地址和品质描述词的有效性。
 * 
 * @param info 单点信息结构，不能为NULL
 * @return 验证结果
 *         - 1: 单点信息有效
 *         - 0: 单点信息无效
 * 
 * @note 验证地址和品质描述词的有效性
 * @warning 输入指针不能为NULL
 * 
 * @see is_info_address_valid
 * @see is_siq_valid
 * @see single_point_info_t
 */
int validate_single_point_info(const single_point_info_t* info);

/**
 * @brief 双点信息转换为字节数组
 * @details 将双点信息结构转换为104协议规定的字节数组格式。
 *          按照协议规范进行数据序列化。
 * 
 * @param info 双点信息结构，不能为NULL
 * @param buffer 输出缓冲区，不能为NULL
 * @param buffer_size 缓冲区大小
 * @param actual_length 实际写入长度输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 缓冲区太小
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 * 
 * @note 按照104协议规范进行数据序列化
 * @warning 缓冲区大小必须足够容纳转换后的数据
 * 
 * @see bytes_to_double_point_info
 * @see validate_double_point_info
 * @see buffer_write_u32
 * @see buffer_write_u8
 */
protocol_error_t double_point_info_to_bytes(const double_point_info_t* info,
                                           u8* buffer, u32 buffer_size, u32* actual_length);

/**
 * @brief 字节数组转换为双点信息
 * @details 将104协议规定的字节数组格式转换为双点信息结构。
 *          按照协议规范进行数据反序列化。
 * 
 * @param data 字节数组，不能为NULL
 * @param length 数据长度
 * @param info 双点信息结构输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 数据长度不足
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 * 
 * @note 按照104协议规范进行数据反序列化
 * @warning 数据长度必须足够容纳双点信息
 * 
 * @see double_point_info_to_bytes
 * @see validate_double_point_info
 * @see buffer_read_u32
 * @see buffer_read_u8
 */
protocol_error_t bytes_to_double_point_info(const u8* data, u32 length,
                                           double_point_info_t* info);

/**
 * @brief 验证双点信息
 * @details 验证双点信息结构的完整性和有效性。
 *          检查地址和品质描述词的有效性。
 * 
 * @param info 双点信息结构，不能为NULL
 * @return 验证结果
 *         - 1: 双点信息有效
 *         - 0: 双点信息无效
 * 
 * @note 验证地址和品质描述词的有效性
 * @warning 输入指针不能为NULL
 * 
 * @see is_info_address_valid
 * @see is_diq_valid
 * @see double_point_info_t
 */
int validate_double_point_info(const double_point_info_t* info);

/**
 * @brief 带时标的单点信息转换为字节数组
 * @details 将带时标的单点信息结构转换为104协议规定的字节数组格式。
 *          按照协议规范进行数据序列化，包含时间戳信息。
 * 
 * @param info 带时标的单点信息结构，不能为NULL
 * @param buffer 输出缓冲区，不能为NULL
 * @param buffer_size 缓冲区大小
 * @param actual_length 实际写入长度输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 缓冲区太小
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_TIMESTAMP: 时间戳无效
 * 
 * @note 按照104协议规范进行数据序列化，包含时间戳
 * @warning 缓冲区大小必须足够容纳转换后的数据
 * 
 * @see bytes_to_single_point_info_with_time
 * @see validate_single_point_info_with_time
 * @see buffer_write_u32
 * @see buffer_write_u8
 * @see memcpy
 */
protocol_error_t single_point_info_with_time_to_bytes(const single_point_info_with_time_t* info,
                                                     u8* buffer, u32 buffer_size, u32* actual_length);

/**
 * @brief 字节数组转换为带时标的单点信息
 * @details 将104协议规定的字节数组格式转换为带时标的单点信息结构。
 *          按照协议规范进行数据反序列化，包含时间戳信息。
 * 
 * @param data 字节数组，不能为NULL
 * @param length 数据长度
 * @param info 带时标的单点信息结构输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 数据长度不足
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_TIMESTAMP: 时间戳无效
 * 
 * @note 按照104协议规范进行数据反序列化，包含时间戳
 * @warning 数据长度必须足够容纳带时标的单点信息
 * 
 * @see single_point_info_with_time_to_bytes
 * @see validate_single_point_info_with_time
 * @see buffer_read_u32
 * @see buffer_read_u8
 * @see memcpy
 */
protocol_error_t bytes_to_single_point_info_with_time(const u8* data, u32 length,
                                                     single_point_info_with_time_t* info);

/**
 * @brief 验证带时标的单点信息
 * @details 验证带时标的单点信息结构的完整性和有效性。
 *          检查地址、品质描述词和时间戳的有效性。
 * 
 * @param info 带时标的单点信息结构，不能为NULL
 * @return 验证结果
 *         - 1: 带时标的单点信息有效
 *         - 0: 带时标的单点信息无效
 * 
 * @note 验证地址、品质描述词和时间戳的有效性
 * @warning 输入指针不能为NULL
 * 
 * @see is_info_address_valid
 * @see is_siq_valid
 * @see is_cp56time2a_valid
 * @see single_point_info_with_time_t
 */
int validate_single_point_info_with_time(const single_point_info_with_time_t* info);

/**
 * @brief 带时标的双点信息转换为字节数组
 * @details 将带时标的双点信息结构转换为104协议规定的字节数组格式。
 *          按照协议规范进行数据序列化，包含时间戳信息。
 * 
 * @param info 带时标的双点信息结构，不能为NULL
 * @param buffer 输出缓冲区，不能为NULL
 * @param buffer_size 缓冲区大小
 * @param actual_length 实际写入长度输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 缓冲区太小
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_TIMESTAMP: 时间戳无效
 * 
 * @note 按照104协议规范进行数据序列化，包含时间戳
 * @warning 缓冲区大小必须足够容纳转换后的数据
 * 
 * @see bytes_to_double_point_info_with_time
 * @see validate_double_point_info_with_time
 * @see buffer_write_u32
 * @see buffer_write_u8
 * @see memcpy
 */
protocol_error_t double_point_info_with_time_to_bytes(const double_point_info_with_time_t* info,
                                                     u8* buffer, u32 buffer_size, u32* actual_length);

/**
 * @brief 字节数组转换为带时标的双点信息
 * @details 将104协议规定的字节数组格式转换为带时标的双点信息结构。
 *          按照协议规范进行数据反序列化，包含时间戳信息。
 * 
 * @param data 字节数组，不能为NULL
 * @param length 数据长度
 * @param info 带时标的双点信息结构输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 数据长度不足
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_TIMESTAMP: 时间戳无效
 * 
 * @note 按照104协议规范进行数据反序列化，包含时间戳
 * @warning 数据长度必须足够容纳带时标的双点信息
 * 
 * @see double_point_info_with_time_to_bytes
 * @see validate_double_point_info_with_time
 * @see buffer_read_u32
 * @see buffer_read_u8
 * @see memcpy
 */
protocol_error_t bytes_to_double_point_info_with_time(const u8* data, u32 length,
                                                     double_point_info_with_time_t* info);

/**
 * @brief 验证带时标的双点信息
 * @details 验证带时标的双点信息结构的完整性和有效性。
 *          检查地址、品质描述词和时间戳的有效性。
 * 
 * @param info 带时标的双点信息结构，不能为NULL
 * @return 验证结果
 *         - 1: 带时标的双点信息有效
 *         - 0: 带时标的双点信息无效
 * 
 * @note 验证地址、品质描述词和时间戳的有效性
 * @warning 输入指针不能为NULL
 * 
 * @see is_info_address_valid
 * @see is_diq_valid
 * @see is_cp56time2a_valid
 * @see double_point_info_with_time_t
 */
int validate_double_point_info_with_time(const double_point_info_with_time_t* info);

/**
 * @brief 故障事件信息转换为字节数组
 * @details 将故障事件信息结构转换为104协议规定的字节数组格式。
 *          按照协议规范进行数据序列化，包含故障类型、状态和时间戳信息。
 * 
 * @param fault_info 故障事件信息结构，不能为NULL
 * @param buffer 输出缓冲区，不能为NULL
 * @param buffer_size 缓冲区大小
 * @param actual_length 实际写入长度输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 缓冲区太小
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_TIMESTAMP: 时间戳无效
 * 
 * @note 按照104协议规范进行数据序列化，包含故障信息
 * @warning 缓冲区大小必须足够容纳转换后的数据
 * 
 * @see bytes_to_fault_event_info
 * @see validate_fault_event_info
 * @see buffer_write_u32
 * @see buffer_write_u8
 * @see memcpy
 */
protocol_error_t fault_event_info_to_bytes(const fault_event_info_t* fault_info,
                                          u8* buffer, u32 buffer_size, u32* actual_length);

/**
 * @brief 字节数组转换为故障事件信息
 * @details 将104协议规定的字节数组格式转换为故障事件信息结构。
 *          按照协议规范进行数据反序列化，包含故障类型、状态和时间戳信息。
 * 
 * @param data 字节数组，不能为NULL
 * @param length 数据长度
 * @param fault_info 故障事件信息结构输出，不能为NULL
 * @return 协议错误码
 *         - PROTOCOL_SUCCESS: 转换成功
 *         - PROTOCOL_ERROR_NULL_POINTER: 空指针错误
 *         - PROTOCOL_ERROR_BUFFER_TOO_SMALL: 数据长度不足
 *         - PROTOCOL_ERROR_ASDU_INVALID_ADDRESS: 地址无效
 *         - PROTOCOL_ERROR_DATA_INVALID_TIMESTAMP: 时间戳无效
 * 
 * @note 按照104协议规范进行数据反序列化，包含故障信息
 * @warning 数据长度必须足够容纳故障事件信息
 * 
 * @see fault_event_info_to_bytes
 * @see validate_fault_event_info
 * @see buffer_read_u32
 * @see buffer_read_u8
 * @see memcpy
 */
protocol_error_t bytes_to_fault_event_info(const u8* data, u32 length,
                                          fault_event_info_t* fault_info);

/**
 * @brief 验证故障事件信息
 * @details 验证故障事件信息结构的完整性和有效性。
 *          检查地址、故障类型、故障状态和时间戳的有效性。
 * 
 * @param fault_info 故障事件信息结构，不能为NULL
 * @return 验证结果
 *         - 1: 故障事件信息有效
 *         - 0: 故障事件信息无效
 * 
 * @note 验证地址、故障类型、故障状态和时间戳的有效性
 * @warning 输入指针不能为NULL
 * 
 * @see is_info_address_valid
 * @see is_cp56time2a_valid
 * @see fault_event_info_t
 */
int validate_fault_event_info(const fault_event_info_t* fault_info);

/* ==================== 辅助函数 ==================== */

/**
 * @brief 获取单点信息大小
 * @details 获取单点信息结构在字节数组中的大小。
 *          用于缓冲区大小计算。
 * 
 * @return 单点信息大小（字节）
 * 
 * @note 单点信息大小：4字节（地址3字节 + SIQ1字节）
 * @warning 此函数是线程安全的
 * 
 * @see get_double_point_info_size
 * @see get_single_point_info_with_time_size
 * @see single_point_info_t
 */
u32 get_single_point_info_size(void);

/**
 * @brief 获取双点信息大小
 * @details 获取双点信息结构在字节数组中的大小。
 *          用于缓冲区大小计算。
 * 
 * @return 双点信息大小（字节）
 * 
 * @note 双点信息大小：4字节（地址3字节 + DIQ1字节）
 * @warning 此函数是线程安全的
 * 
 * @see get_single_point_info_size
 * @see get_double_point_info_with_time_size
 * @see double_point_info_t
 */
u32 get_double_point_info_size(void);

/**
 * @brief 获取带时标的单点信息大小
 * @details 获取带时标的单点信息结构在字节数组中的大小。
 *          用于缓冲区大小计算。
 * 
 * @return 带时标的单点信息大小（字节）
 * 
 * @note 带时标的单点信息大小：11字节（地址3字节 + SIQ1字节 + 时间戳7字节）
 * @warning 此函数是线程安全的
 * 
 * @see get_single_point_info_size
 * @see get_double_point_info_with_time_size
 * @see single_point_info_with_time_t
 */
u32 get_single_point_info_with_time_size(void);

/**
 * @brief 获取带时标的双点信息大小
 * @details 获取带时标的双点信息结构在字节数组中的大小。
 *          用于缓冲区大小计算。
 * 
 * @return 带时标的双点信息大小（字节）
 * 
 * @note 带时标的双点信息大小：11字节（地址3字节 + DIQ1字节 + 时间戳7字节）
 * @warning 此函数是线程安全的
 * 
 * @see get_double_point_info_size
 * @see get_single_point_info_with_time_size
 * @see double_point_info_with_time_t
 */
u32 get_double_point_info_with_time_size(void);

/**
 * @brief 获取故障事件信息大小
 * @details 获取故障事件信息结构在字节数组中的大小。
 *          用于缓冲区大小计算。
 * 
 * @return 故障事件信息大小（字节）
 * 
 * @note 故障事件信息大小：12字节（地址3字节 + 故障类型1字节 + 故障状态1字节 + 时间戳7字节）
 * @warning 此函数是线程安全的
 * 
 * @see get_single_point_info_with_time_size
 * @see get_double_point_info_with_time_size
 * @see fault_event_info_t
 */
u32 get_fault_event_info_size(void);

#ifdef __cplusplus
}
#endif

#endif /* TELEMETRY_DATA_H */
